/**
 * // Copyright (c) 2023 KylinOS. All rights reserved.
 * //
 * // This program is free software; you can redistribute it
 * // and/or modify it under the terms of the GNU General Public
 * // License (version 2) as published by the FSF - Free Software
 * // Foundation*
 */

/**
 * // Copyright (C) 2023 ben LG <guoliben@kylinos.cn>
 */

package docextractor

import (
	"bufio"
	"bytes"
	"io"
	"io/ioutil"
	"os"
	"time"
	"unsafe"

	"gitee.com/guoliben/kydlp-doctotext/extractor_interface"
	"gitee.com/guoliben/kydlp-doctotext/utils/ctrlledprint"
)

type DocText struct{}

// 解析 text 文档内容
func (extractor DocText) GetTextFromFile(path string, size int64, filetype string, depth int) ([]byte, string) {
	ctrlledprint.Println("提取文本: 正在解析text(file)文档...")

	start := time.Now()

	content, err := ioutil.ReadFile(path)
	if err != nil {
		ctrlledprint.Println("Error reading text file:", err)
		return nil, ""
	}
	text := content

	elapsed := time.Since(start)

	return text, elapsed.String()
}

func (extractor DocText) GetTextFromBuffer(path string, data []byte, size int64, filetype string, depth int) ([]byte, string) {
	ctrlledprint.Println("提取文本: 正在解析text(buffer)文档...")

	start := time.Now()

	text := data

	elapsed := time.Since(start)

	return text, elapsed.String()
}

func (extractor DocText) GetStringTextFromFile(path string, size int64, filetype string, depth int) (string, string) {
	ctrlledprint.Println("提取文本: 正在解析text(file)文档...")

	start := time.Now()

	content, err := ioutil.ReadFile(path)
	if err != nil {
		ctrlledprint.Println("Error reading text file:", err)
		return "", ""
	}
	// 解决内存重复问题
	text := *(*string)(unsafe.Pointer(&content))

	elapsed := time.Since(start)

	return text, elapsed.String()
}

func (extractor DocText) GetStringTextFromBuffer(path string, data []byte, size int64, filetype string, depth int) (string, string) {
	ctrlledprint.Println("提取文本: 正在解析text(buffer)文档...")

	start := time.Now()

	// 解决内存重复问题
	text := *(*string)(unsafe.Pointer(&data))

	elapsed := time.Since(start)

	return text, elapsed.String()
}

const maxChunkSize = 1 * 1024 * 1024 // 1MB

func txtGetTextFromFile(path string, reader *bufio.Reader, size int64, filetype string, depth int, callback extractor_interface.OnTextDetectHandler, context interface{}) (int, []byte, string) {
	var retcode int
	var totaltext bytes.Buffer
	var totalelapsed time.Duration

	start := time.Now()

	// 创建一个缓冲区：chunk一次性初始化，避免无限创建
	chunk := make([]byte, maxChunkSize)

	// 按块读取文件内容
	for {
		// 创建一个缓冲区
		//chunk := make([]byte, maxChunkSize)

		// 读取数据到缓冲区
		n, err := reader.Read(chunk)
		if err != nil {
			if err != io.EOF {
				ctrlledprint.Println("Error reading chunk of text file:", err)
				return 0, nil, ""
			}
		}
		// 如果到达文件末尾，退出循环
		if n == 0 {
			break
		}

		// 判断chunk的最后一个字节是否是行的结束符
		lastbyte := chunk[n-1]
		if lastbyte != '\r' && lastbyte != '\n' {
			// 处理不完整的行，向前读取直到遇见行结束为止，以解决中文字符被截断导致的乱码问题
			remaining, err := reader.ReadBytes('\n')
			if err != nil {
				if err != io.EOF {
					ctrlledprint.Println("Error reading remaining bytes of line of text file:", err)
					return 0, nil, ""
				}
			}
			chunk = append(chunk[:n], remaining...)
		}

		if callback != nil {
			retcode, _ = callback(path, size, filetype, chunk, int64(len(chunk)), context)
			if retcode == 0 {
				continue
			} else {
				break
			}
		}

		// chunk空间释放
		chunk = nil

		// 如果到达文件末尾，退出循环
		if err == io.EOF {
			break
		}
	}

	elapsed := time.Since(start)

	totalelapsed += elapsed

	return retcode, totaltext.Bytes(), totalelapsed.String()
}

func txtGetStringTextFromFile(path string, reader *bufio.Reader, size int64, filetype string, depth int, callback extractor_interface.OnStringTextDetectHandler, context interface{}) (int, string, string) {
	var retcode int
	var totaltext bytes.Buffer
	var totalelapsed time.Duration

	start := time.Now()

	// 创建一个缓冲区：chunk一次性初始化，避免无限创建
	chunk := make([]byte, maxChunkSize)

	// 按块读取文件内容
	for {
		// 创建一个缓冲区
		//chunk := make([]byte, maxChunkSize)

		// 读取数据到缓冲区
		n, err := reader.Read(chunk)
		if err != nil {
			if err != io.EOF {
				ctrlledprint.Println("Error reading chunk of text file:", err)
				return 0, "", ""
			}
		}
		// 如果到达文件末尾，退出循环
		if n == 0 {
			break
		}

		// 判断chunk的最后一个字节是否是行的结束符
		lastbyte := chunk[n-1]
		if lastbyte != '\r' && lastbyte != '\n' {
			// 处理不完整的行，向前读取直到遇见行结束为止，以解决中文字符被截断导致的乱码问题
			remaining, err := reader.ReadBytes('\n')
			if err != nil {
				if err != io.EOF {
					ctrlledprint.Println("Error reading remaining bytes of line of text file:", err)
					return 0, "", ""
				}
			}
			chunk = append(chunk[:n], remaining...)
		}

		if callback != nil {
			retcode, _ = callback(path, size, filetype, *(*string)(unsafe.Pointer(&chunk)), int64(len(chunk)), context)
			if retcode == 0 {
				continue
			} else {
				break
			}
		}

		// chunk空间释放
		chunk = nil

		// 如果到达文件末尾，退出循环
		if err == io.EOF {
			break
		}
	}

	elapsed := time.Since(start)

	totalelapsed += elapsed

	return retcode, totaltext.String(), totalelapsed.String()
}

func (extractor DocText) GetTextFromFileWithCallback(path string, size int64, filetype string, depth int, callback extractor_interface.OnTextDetectHandler, context interface{}) (int, []byte, string) {
	ctrlledprint.Println("提取文本: 正在解析text(file)文档...")

	file, err := os.Open(path)
	if err != nil {
		ctrlledprint.Println("Error opening text file:", err)
		return 0, nil, ""
	}
	defer file.Close()

	reader := bufio.NewReaderSize(file, maxChunkSize)

	return txtGetTextFromFile(path, reader, size, filetype, depth, callback, context)
}

func (extractor DocText) GetTextFromBufferWithCallback(path string, data []byte, size int64, filetype string, depth int, callback extractor_interface.OnTextDetectHandler, context interface{}) (int, []byte, string) {
	ctrlledprint.Println("提取文本: 正在解析text(buffer)文档...")

	rd := bytes.NewReader(data)

	reader := bufio.NewReaderSize(rd, maxChunkSize)

	return txtGetTextFromFile(path, reader, size, filetype, depth, callback, context)
}

func (extractor DocText) GetStringTextFromFileWithCallback(path string, size int64, filetype string, depth int, callback extractor_interface.OnStringTextDetectHandler, context interface{}) (int, string, string) {
	ctrlledprint.Println("提取文本: 正在解析text(file)文档...")

	file, err := os.Open(path)
	if err != nil {
		ctrlledprint.Println("Error opening text file:", err)
		return 0, "", ""
	}
	defer file.Close()

	reader := bufio.NewReaderSize(file, maxChunkSize)

	return txtGetStringTextFromFile(path, reader, size, filetype, depth, callback, context)
}

func (extractor DocText) GetStringTextFromBufferWithCallback(path string, data []byte, size int64, filetype string, depth int, callback extractor_interface.OnStringTextDetectHandler, context interface{}) (int, string, string) {
	ctrlledprint.Println("提取文本: 正在解析text(buffer)文档...")

	rd := bytes.NewReader(data)

	reader := bufio.NewReaderSize(rd, maxChunkSize)

	return txtGetStringTextFromFile(path, reader, size, filetype, depth, callback, context)
}
